/**
 * 
 */
package bancosys.tec.persist.validator;

import java.lang.reflect.TypeVariable;
import java.util.LinkedList;
import java.util.List;

import jmine.tec.annotations.Unmodifiable;
import jmine.tec.persist.PersistMessages;
import jmine.tec.utils.reflection.GenericTypeResolverImpl;
import bancosys.tec.persist.Persistable;
import bancosys.tec.persist.exception.PersistenceException;

/**
 * @author Fabio Sakiyama
 * @created 27/04/2011
 * @param <T>
 */
@Unmodifiable
public abstract class AbstractValidator<T extends Persistable> implements Validator<T> {

    private final Class<T> targetClass;

    /**
     * C'tor
     */
    @SuppressWarnings("unchecked")
    public AbstractValidator() {
        try {
            TypeVariable<Class<AbstractValidator>> typeVariable = AbstractValidator.class.getTypeParameters()[0];
            this.targetClass =
                    (Class<T>) GenericTypeResolverImpl.getInstance().resolveTypeForFixedGenericType(typeVariable, this.getClass());
        } catch (IllegalArgumentException e) {
            throw new PersistenceException(PersistMessages.INVALID_BEAN_IMPLEMENTATION.create(this.getClass().getName()), e);
        }
    }

    /**
     * C'tor
     * 
     * @param targetClass the target type
     */
    public AbstractValidator(Class<T> targetClass) {
        super();
        this.targetClass = targetClass;
    }

    /**
     * Dadas duas listas, se uma delas for <code>null</code>, devolve a outra, se ambas forem <code>null</code> devolve <code>null</code>,
     * se ambas forem diferentes de <code>null</code> devolve uma terceira lista com o conteudo das duas.
     * 
     * @param errors lista 1
     * @param moreErrors lista 2
     * @return soma das duas listas ou <code>null</code>.
     */
    protected List<ValidationError> sumErrors(final List<ValidationError> errors, final List<ValidationError> moreErrors) {
        if (errors == null && moreErrors == null) {
            return null;
        }
        if (errors != null && moreErrors == null) {
            return errors;
        }
        if (errors == null && moreErrors != null) {
            return moreErrors;
        }
        this.validateAddError(errors, moreErrors);
        return errors;
    }

    /**
     * Valida se a mensagem de erro já esta na lista, para que não sejam incluídas mensagens duplicadas
     * 
     * @param errors - lista com as mensagens de erros já gravadas
     * @param error - nova mensagem de erro, para ser verificado
     * @return lista com as mensagens de erro
     */
    private List<ValidationError> validateAddError(final List<ValidationError> errors, final ValidationError error) {
        for (final ValidationError validationError : errors) {
            final String msgErro = validationError.getMessage();
            if (msgErro.equals(error.getMessage())) {
                return errors;
            }
        }
        errors.add(error);
        return errors;
    }

    /**
     * Adiciona uma Lista de Erros em outra. Validando se não estão sendo incluídos erros duplicados
     * 
     * @param errors - Lista principal, os erros da outra lista serão incluídos nesta.
     * @param moreErrors - Lista que vai ser adicionada na outra lista.
     * @return Lista com todos os erros não duplicados.
     */
    private List<ValidationError> validateAddError(final List<ValidationError> errors, final List<ValidationError> moreErrors) {
        for (final ValidationError error : moreErrors) {
            this.validateAddError(errors, error);
        }
        return errors;
    }

    /**
     * Adicionar um erro a lista de erros, se o erro for <code>null</code> não faz nada, <BR>
     * se a lista for <code>null</code> aloca a lista se necessario.
     * 
     * @param errors lista de erros
     * @param error erro
     * @return lista alterada
     */
    public List<ValidationError> addError(final List<ValidationError> errors, final ValidationError error) {
        List<ValidationError> errorList = errors;
        if (error == null) {
            return errorList;
        }
        if (errorList == null) {
            errorList = new LinkedList<ValidationError>();
        }
        this.validateAddError(errorList, error);
        return errorList;
    }

    /**
     * Valida insert.
     * 
     * @param bean bean.
     * @return erros ou null.
     */
    public List<ValidationError> validateInsert(T bean) {
        return null;
    }

    /**
     * Valida autorizacao do insert.
     * 
     * @param bean bean.
     * @return erros ou null.
     */
    public List<ValidationError> validateUpdate(T bean) {
        return null;
    }

    /**
     * Valida remove.
     * 
     * @param bean bean.
     * @return erros ou null.
     */
    public List<ValidationError> validateRemove(T bean) {
        return null;
    }

    /**
     * Valida autorizacao do insert.
     * 
     * @param bean bean.
     * @return erros ou null.
     */
    public List<ValidationError> validateInsertAuthorization(T bean) {
        return null;
    }

    /**
     * Valida autorizacao do update.
     * 
     * @param bean bean.
     * @return erros ou null.
     */
    public List<ValidationError> validateUpdateAuthorization(T bean) {
        return null;
    }

    /**
     * Valida autorizacao do remove.
     * 
     * @param bean bean.
     * @return erros ou null.
     */
    public List<ValidationError> validateRemoveAuthorization(T bean) {
        return null;
    }

    /**
     * @return Classe configurada como target para este validator através de generics.
     */
    public final Class<? extends T> getTargetClass() {
        return this.targetClass;
    }

}
